#include "Program.h"
#include <iostream>
#include <cassert>
#include "Routine.h"
#include "Instruction.h"
#include "Memory.h"
#include "Variable.h"
#include "Expression.h"
#include "DataHolderGarbageCollector.h"
#include "CREATE_VARIABLE.h"
#include "ASSIGN_VARIABLE.h"

using namespace std;

Program::Program(const string& name) {
	assert(name != "");
	
	m_name = name;
	m_memory = new Memory();
}

Program::~Program() {
	
	for (Routines::iterator i = m_routines.begin(); i != m_routines.end(); ++i) {
		delete (*i);
	}
	
	delete m_memory;
}

void Program::run() {
	assert(m_routines.size() > 0);
	
	m_memory->reset();
	executeRoutine(0);	
}

void Program::executeRoutine(int index) {
	assert(index >= 0);
	assert(index <= m_routines.size() - 1);
	
	Routine* m_currentRoutine = m_routines[index];
	
	for (int i = 0; i < m_currentRoutine->nrOfInstructions(); ++i) {
		Instruction* currentInstruction = m_currentRoutine->getInstruction(i);
		
		if (currentInstruction->getType() == Instruction::CREATE_VARIABLE) {
			executeInstruction_CREATE_VARIABLE(currentInstruction);
		}
		else if (currentInstruction->getType() == Instruction::ASSIGN_VARIABLE) {
			executeInstruction_ASSIGN_VARIABLE(currentInstruction);
		}
	}
}

void Program::addRoutine(Routine* routine) {
	assert(routine);
	
	for (Routines::iterator i = m_routines.begin(); i != m_routines.end(); ++i) {
		Routine* r = (*i);
		assert(routine != r);
	}
	
	m_routines.push_back(routine);
}

void Program::executeInstruction_CREATE_VARIABLE(Instruction* currentInstruction) {
	assert(currentInstruction);
	
	CREATE_VARIABLE_Instruction* createVar = 
		dynamic_cast<CREATE_VARIABLE_Instruction*> (currentInstruction);
	
	assert(currentInstruction);
	
	Data* initData = calculateValueOfDataHolder(createVar->getVariableExpression());	
	Variable* newVariable = Variable::createVariable(createVar->getVariableName(), initData);
	m_memory->addVariable(newVariable);	
}

void Program::executeInstruction_ASSIGN_VARIABLE(Instruction* currentInstruction) {
	assert(currentInstruction);
	
	ASSIGN_VARIABLE_Instruction* assignVar = 
		dynamic_cast<ASSIGN_VARIABLE_Instruction*> (currentInstruction);
	
	assert(currentInstruction);
	
	Data* assignData = calculateValueOfDataHolder(assignVar->getVariableExpression());	
	Variable* variable = m_memory->getVariable(assignVar->getVariableName());
	assert(variable);
	variable->setData(assignData);
}

Data* Program::calculateValueOfDataHolder(DataHolder* dataHolder) {
	
	assert(dataHolder);
	
	Data* data = NULL;
	
	if (dataHolder->getType() == DataHolder::BOOLEAN) {
		data = Data::createBoolean(dataHolder->getBooleanValue());
	}
	else if (dataHolder->getType() == DataHolder::INT) {
		data = Data::createInt(dataHolder->getIntValue());
	}
	else if (dataHolder->getType() == DataHolder::FLOAT) {
		data = Data::createFloat(dataHolder->getFloatValue());
	}
	else if (dataHolder->getType() == DataHolder::STRING) {
		data = Data::createString(dataHolder->getStringValue());
	}
	else if (dataHolder->getType() == DataHolder::VARIABLE_NAME) {
		data = Data::createVariableName(dataHolder->getVariableName());
	}
	
	assert(data);

	return data;
}
